<?php
//   +----------------------------------------------------------------------
//   | Copyright (c) 2015-2025 http://www.hdphp.cn All rights reserved.
//   | Licensed ( http://www.hdphp.cn/licenses/ )
//   | Author: Jack <sophia2152@qq.com>
//   | 官方网址: http://www.hdphp.cn
//   | 这不是一个自由软件！您只能在不用于商业目的的前提下对程序代码进行修改和使用。
//   | 任何企业和个人不允许对程序代码以任何形式任何目的再发布。
//   +----------------------------------------------------------------------
namespace hdphp\services;

use think\facade\Cache;

/**
 * Class：数据库锁服务类
 * 创建人：Jack
 * 创建时间：2024/1/11 11:20
 * @package hdphp\services
 */
class LockService
{
    /**
     * 注释：执行
     * 创建人：Jack
     * 创建时间：2024/1/11 11:20
     * @param     $key
     * @param     $fn
     * @param int $ex
     * @return mixed
     */
    public function exec($key, $fn, int $ex = 6)
    {
        try {
            $this->lock($key, $key, $ex);
            return $fn();
        } finally {
            $this->unlock($key, $key);
        }
    }

    /**
     * 注释：tryLock
     * 创建人：Jack
     * 创建时间：2024/1/11 11:21
     * @param $key
     * @param $value
     * @param $ex
     * @return mixed
     */
    public function tryLock($key, $value = '1', $ex = 6)
    {
        return Cache::store('redis')->handler()->set('lock_' . $key, $value, ["NX", "EX" => $ex]);
    }

    /**
     * 注释：lock
     * 创建人：Jack
     * 创建时间：2024/1/11 11:21
     * @param $key
     * @param $value
     * @param $ex
     * @return true|void
     */
    public function lock($key, $value = '1', $ex = 6)
    {
        if ($this->tryLock($key, $value, $ex)) {
            return true;
        }
        usleep(200);
        $this->lock($key, $value, $ex);
    }

    /**
     * 注释：unlock
     * 创建人：Jack
     * 创建时间：2024/1/11 11:21
     * @param $key
     * @param $value
     * @return bool
     */
    public function unlock($key, $value = '1')
    {
        $script = <<< EOF
if (redis.call("get", "lock_" .. KEYS[1]) == ARGV[1]) then
	return redis.call("del", "lock_" .. KEYS[1])
else
	return 0
end
EOF;
        return Cache::store('redis')->handler()->eval($script, [$key, $value], 1) > 0;
    }
}